package com.zcsy.commons.util;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.zcsy.commons.model.RequestConstants;

/**
 * @Title: WeChatUtils.java
 * @Package： com.zcsy.commons.util
 * @Description: 微信公众号的帮助类 用于动态生成access_token
 *
 * @author tanghan barbadosliy@163.com
 * @Copyright: 2015 武汉中财数元信息技术有限公司 All rights reserved.
 * @date 2015年9月17日
 * @version 1.0
 * @see JDK1.7.0_75
 * @since
 */
public class WeChatUtils {
	
	/**
	 * 将accessToken缓存起来，避免频繁的请求访问Token<br>
	 */
	private static final String ACCESS_TOKEN_KEY = "fyAccessToken";

	/**
	 * @Description 根据网页授权回调的Code获取accessToken及OpenId等<br>
	 * 
	 * @param code
	 * @return { 
	 *           "access_token":"ACCESS_TOKEN", 网页授权凭证，和基础的 AccessToken不同
	 *           "expires_in":7200,
	 *           "refresh_token":"REFRESH_TOKEN", 
	 *           "openid":"OPENID",
	 *           "scope":"SCOPE", 
	 *           "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
	 *         }
	 * @author tanghan barbadosliy@163.com
	 * @date 2015年11月4日
	 * @see
	 */
	public static JSONObject getOAuth2ByCode(String code) {
		JSONObject json = null;
		try{
			PropertiesUtil util = new PropertiesUtil("config.properties");
			String appId = util.getProperty("appId");
			String appSecret = util.getProperty("appSecret");
			
			StringBuffer url = new StringBuffer("https://api.weixin.qq.com/sns/oauth2/access_token?");
			url.append("appid=").append(appId);
			url.append("&secret=").append(appSecret);
			url.append("&code=").append(code);
			url.append("&grant_type=authorization_code");
			
			String content = HttpUtil.sendGet(url.toString(), HttpUtil.UTF8);
			json = JSONObject.parseObject(content);
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return json;
	}
	
	/**
	 * @Description 根据OpenId获取用户基本信息<br>
	 * 
	 * @param openId 用户 OpenId
	 * @param accessToken 访问授权
	 * @return  用户信息
	 * @author tanghan barbadosliy@163.com
	 * @date 2015年11月4日
	 * @see 
	 */
	public static JSONObject getUserInfoByOpenId(String openId,String accessToken){
		JSONObject userInfo = null; 
		try {
			StringBuffer url = new StringBuffer("https://api.weixin.qq.com/sns/userinfo?");
			url.append("access_token=").append(accessToken);
			url.append("&openid=").append(openId).append("&lang=zh_CN");
			String content = HttpUtil.sendGet(url.toString(), HttpUtil.UTF8);
			userInfo = JSONObject.parseObject(content);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return userInfo;
	}
	
	/**
	 * @Description 公众平台的API调用所需的access_token<br>
	 *              1、为了保密appsecrect，第三方需要一个access_token获取和刷新的中控服务器。
	 *              而其他业务逻辑服务器所使用的access_token均来自于该中控服务器
	 *              ，不应该各自去刷新，否则会造成access_token覆盖而影响业务；<br>
	 *              2、目前access_token的有效期通过返回的expire_in来传达，目前是7200秒之内的值。
	 *              中控服务器需要根据这个有效时间提前去刷新新access_token *
	 *              。在刷新过程中，中控服务器对外输出的依然是老access_token
	 *              ，此时公众平台后台会保证在刷新短时间内，新老access_token都可用，这保证了第三方业务的平滑过渡；<br>
	 *              3、access_token的有效时间可能会在未来有调整，所以中控服务器不仅需要内部定时主动刷新，
	 *              还需要提供被动刷新access_token的接口
	 *              ，这样便于业务服务器在API调用获知access_token已超时的情况下，可以触发access_token的刷新流程。<br/>
	 * 
	 * @return
	 * @author tanghan barbadosliy@163.com
	 * @date 2015年9月17日
	 * @see
	 */
	public static String getAccessToken() {
		PropertiesUtil util = new PropertiesUtil("config.properties");
		String appId = util.getProperty("appId");
		String appSecret = util.getProperty("appSecret");

		StringBuffer url = new StringBuffer(
				"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential");
		url.append("&appid=").append(appId);
		url.append("&secret=").append(appSecret);

		String content = HttpUtil.sendGet(url.toString(), HttpUtil.UTF8);
		JSONObject json = JSONObject.parseObject(content);
		return json.getString("access_token");
	}

	public static JSONObject sign(String jsapi_ticket, String url) {
		JSONObject ret = new JSONObject();
		String nonce_str = create_nonce_str();
		String timestamp = create_timestamp();
		String string1;
		String signature = "";

		// 注意这里参数名必须全部小写，且必须有序
		string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str
				+ "&timestamp=" + timestamp + "&url=" + url;
		try {
			MessageDigest crypt = MessageDigest.getInstance("SHA-1");
			crypt.reset();
			crypt.update(string1.getBytes("UTF-8"));
			signature = byteToHex(crypt.digest());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		ret.put("appId", getAppId());
		ret.put("url", url);
		ret.put("jsapi_ticket", jsapi_ticket);
		ret.put("nonceStr", nonce_str);
		ret.put("timestamp", timestamp);
		ret.put("signature", signature);

		return ret;
	}

	private static String byteToHex(final byte[] hash) {
		Formatter formatter = new Formatter();
		for (byte b : hash) {
			formatter.format("%02x", b);
		}
		String result = formatter.toString();
		formatter.close();
		return result;
	}

	/**
	 * @Description 获取公众号对应的APPID>
	 * 
	 * @return 公众号appId
	 * @author tanghan barbadosliy@163.com
	 * @date 2015年9月24日
	 * @see
	 */
	public static String getAppId() {
		PropertiesUtil util = new PropertiesUtil("config.properties");
		String appId = util.getProperty("appId");
		return appId;
	}

	public static String create_nonce_str() {
		return getRandomString(16);
	}
	
    /**
     * @Description 生产固定位数的随机字符串<br>
     * @param length
     * @return 
     * @author tanghan barbadosliy@163.com
     * @date 2015年11月9日
     * @see 
     */
    public static String getRandomString(int length) { //length表示生成字符串的长度  
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";     
        Random random = new Random();     
        StringBuffer sb = new StringBuffer();     
        for (int i = 0; i < length; i++) {     
            int number = random.nextInt(base.length());     
            sb.append(base.charAt(number));     
        }     
        return sb.toString();     
    }     
	public static String create_timestamp() {
		return String.valueOf(System.currentTimeMillis() / 1000);
	}

	public static String createSign(SortedMap<Object,Object> parameters,String key){ 
	    StringBuffer sb = new StringBuffer(); 
	    Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序（升序） 
	    Iterator it = es.iterator(); 
	    while(it.hasNext()) { 
	      Map.Entry entry = (Map.Entry)it.next(); 
	      String k = (String)entry.getKey(); 
	      Object v = entry.getValue(); 
	      if(null != v && !"".equals(v)  
	          && !"sign".equals(k) && !"key".equals(k)) { 
	        sb.append(k + "=" + v + "&"); 
	      } 
	    } 
	    sb.append("key=" + key);
	    
	    String sign = MD5Util.MD5Encode(sb.toString(),"UTF-8").toUpperCase(); 
	    return sign; 
	}
	
	
	public static String createAppSign(SortedMap<Object,Object> parameters,String key) {
        StringBuilder sb = new StringBuilder();

        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序（升序） 
        Iterator it = es.iterator(); 
        while(it.hasNext()) { 
          Map.Entry entry = (Map.Entry)it.next(); 
          String k = (String)entry.getKey(); 
          Object v = entry.getValue(); 
          if(null != v && !"".equals(v)  
              && !"sign".equals(k) && !"key".equals(k)) { 
            sb.append(k + "=" + v + "&"); 
          } 
        }
        
        sb.append("key=");
        sb.append(key);

        String appSign = getMessageDigest(sb.toString().getBytes()).toUpperCase();
        return appSign;
    }
	
	public final static String getMessageDigest(byte[] buffer) {
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(buffer);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }
	
}
